
/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/Flyout.js
*/
var Flyout = function() {
	
};

Flyout.clearVisibleFlyouts = function() {
	if (!this.visibles) {
		return;
	}
	for (var i=0; i<this.visibles.length; i++) {
		this.visibles[i].hide();
	}
	this.visibles = [];
};

Flyout.addVisibleFlyout = function(flyout) {
	this.visibles = this.visibles || [];
	this.visibles.push(flyout);
};


Flyout.prototype.CSS_HIDDEN = "wsodHidden";
Flyout.prototype.CSS_CONTAINER = "flyout";
Flyout.prototype.CSS_SELECTED = "selected";
Flyout.prototype.CSS_SHADOW = "shadow";
Flyout.prototype.CSS_SUB = "sub";

Flyout.prototype.POSITION_BELOW = "below";
Flyout.prototype.POSITION_RIGHT = "right";

Flyout.prototype.addTarget = function(el) {
	this.getShowEvent().addElement(el);
};

Flyout.prototype.getEventManager = function() {
	var em = new EventManager();
	this.getEventManager = function() {
		return em;
	};
	return this.getEventManager();
};

Flyout.prototype.getShowEvent = function() {
	var showEvent = Events.add(null, "mouseup", this.show, this);
	this.getShowEvent = function() {
		return showEvent;
	};
	return this.getShowEvent();
};


Flyout.prototype.getHideEvent = function() {
	var hideEvent = Events.add(null, "mouseout", this.hide, this, null, 1000);
	this.getHideEvent = function() {
		return hideEvent;
	};
	return this.getHideEvent();
};

Flyout.prototype.getPageClickEvent = function() {
	var e = Events.add(null, "mouseup", this.hide, this);
	this.getPageClickEvent = function() {
		return e;
	};
	return this.getPageClickEvent();
};

Flyout.prototype.getMouseOver = function() {
	var mouseOver = Events.add(null, "mouseover", this.show, this);
	this.getMouseOver = function() {
		return mouseOver;
	};
	return this.getMouseOver();
};

Flyout.prototype.getParent = function() {
	return WSDOM.Element.get("wsod");
};

Flyout.prototype.setParent = function(el) {
	this.getParent = function() {
		return el;
	};
};

Flyout.prototype.getFlyoutParent = function() {
	return null;
};

Flyout.prototype.setFlyoutParent = function(flyout) {

	this.setParent(flyout.getContainer());
	this.setPositionTarget(flyout.getContainer());
	
	this.getFlyoutParent = function() {
		return flyout;
	};
	
	WSDOM.Element.addClass(this.getFrame(), this.CSS_SUB);
};

Flyout.prototype.getFrame = function() {
	var frame = WSDOM.Element.create('div', { className: this.CSS_CONTAINER }, [
		WSDOM.Element.create("iframe", { "class": this.CSS_SHADOW, src: "javascript:false;", frameborder: 0 }),
		WSDOM.Element.create("div", { "class": this.CSS_SHADOW }),
		WSDOM.Element.create("ul")
	], this.getParent());
	
	WSDOM.Element.addClass(frame, this.CSS_HIDDEN);
	
	this.getFrame = function() {
		return frame;
	};
	
	return this.getFrame();
};

Flyout.prototype.getShim = function() {
	return this.getFrame().childNodes[0];
};

Flyout.prototype.getShadow = function() {
	return this.getFrame().childNodes[1];
};

Flyout.prototype.getContainer = function() {
	var container = this.getFrame().childNodes[2];
	this.getMouseOver().addElement(container);
	
	this.getContainer = function() {
		return container;
	};
	
	return this.getContainer();
};

Flyout.prototype.setData = function(data) {
	this.data = data;
	this.items = [];
	this.itemsById = {};
	
	var container = this.getContainer();
	
	WSDOM.Element.removeChildNodes(container);

	for (var i=0; i<data.length; i++) {
		
		var flyoutItem = new FlyoutItem(this);
		flyoutItem.setData(data[i]);
		flyoutItem.isLast((i == data.length-1));
		this.items.push(flyoutItem);
		
		if (data[i].id) {
			this.itemsById[data[i].id] = flyoutItem;
		}
		
	};
	
};

Flyout.prototype.isVisible = function() {
	return !WSDOM.Element.hasClass(this.getFrame(), this.CSS_HIDDEN);
}

Flyout.prototype.show = function(e, el) {	
	e.cancel();
	
	this.getHideEvent().clearDelayTimeouts();
	
	if (this.isVisible()) {	
	
		if (el == this.currentTarget) {
			this.currentTarget = null;
			this.hide();
		}
		
		return;
	}
	
	if (!this.getFlyoutParent()) {
		this.currentTarget = el;
		Flyout.clearVisibleFlyouts();
		Flyout.addVisibleFlyout(this);
	}
	
	var frame = this.getFrame();
	
	this.getHideEvent().addElement([frame, el]);
	this.getPageClickEvent().addElement(document);
	
	this.deselectAll();
	
	WSDOM.Element.removeClass(frame, this.CSS_HIDDEN);
	this.position(el);
};

Flyout.prototype.position = function(elTarget) {
	var frame = this.getFrame();
	WSDOM.Element.setStyle(frame, "visibility: hidden");
	
	var target = this.getPositionTarget() || elTarget;
	
	var targetPos = WSDOM.Element.getXY(target);
	var targetSize = WSDOM.Element.getSize(target);
	
	var size = WSDOM.Element.getSize(this.getFrame());
	var offset = WSDOM.Element.getXY(this.getFrame().offsetParent) || { x: 0, y: 0 };
	
	var x = targetPos.x - offset.x;
	var y = targetPos.y - offset.y;
	
	if (this.POSITION_BELOW == this.getPositionStyle()) {
		y += targetSize.height;
	}
	else if (this.POSITION_RIGHT == this.getPositionStyle()) {
		x += targetSize.width;
		y = this.checkVerticalPos(y, elTarget, size, offset);
	}
	
	WSDOM.Element.setXY(
		this.getFrame(), x, y
	);
	
	this.sizeShim(size);
	
	WSDOM.Element.setStyle(frame, "visibility: visible");
};

Flyout.prototype.sizeShim = function(size) {
	// because i can't figure out how to make IE6 do this on its own
	// with the magic of CSS (it's ignoring height 100%)
	// laaaaaaame
	WSDOM.Element.setSize(this.getShim(), size.width, size.height);
	WSDOM.Element.setSize(this.getShadow(), size.width, size.height);
};

Flyout.prototype.checkVerticalPos = function(y, elTarget, size, offset) {
	var elTargetSize = WSDOM.Element.getSize(elTarget);
	var elTargetPos = WSDOM.Element.getXY(elTarget);
	var elTargetBottom = elTargetPos.y + elTargetSize.height - offset.y;
	
	if (elTargetBottom > y + size.height) {
		y = elTargetBottom - size.height;
	}
	
	return y;
};

Flyout.prototype.getPositionStyle = function() {
	return this.POSITION_BELOW;
};

Flyout.prototype.setPositionStyle = function(style) {
	this.getPositionStyle = function() {
		return style
	};
};

Flyout.prototype.getPositionTarget = function() {
	return null;
};

Flyout.prototype.setPositionTarget = function(el) {
	this.getPositionTarget = function() {
		return el;
	};
};

Flyout.prototype.hide = function(e, el) {
	WSDOM.Element.addClass(this.getFrame(), this.CSS_HIDDEN);
	
	//frieken ie6
	this.getFrame().style.visibility = "hidden";
	
	this.hideChildren();
	
	this.getHideEvent().removeAllElements();
	this.getPageClickEvent().removeAllElements();
};

Flyout.prototype.deselectAll = function() {
	for (var i=0; i<this.items.length; i++) {
		WSDOM.Element.removeClass(this.items[i].getContainer(), this.CSS_SELECTED);
	};
}

Flyout.prototype.hideChildren = function(item) {
	for (var i=0; i<this.items.length; i++) {
		if (item == this.items[i]) {
			continue;
		}
		
		WSDOM.Element.removeClass(this.items[i].getContainer(), this.CSS_SELECTED);
		
		if (this.items[i].child) {
			this.items[i].child.hide();
		}
	}
};

Flyout.prototype.hover = function(item) {
	this.getHideEvent().clearDelayTimeouts();
	this.deselectAll();
	this.hideChildren();

	var parent = this;
	while (parent = parent.getFlyoutParent()) {
		parent.getHideEvent().clearDelayTimeouts();
	}
};

Flyout.prototype.onSelect = function() {
	var event = this.getEventManager().add("flyoutItemSelected");
	this.onSelect = function() {
		return event;
	};
	
	this.onSelect().addListener(function(e, item) {
		
		
		if (item.child) {
			// we do nothing on click
			// prevent propagation?
			return;
		}
		
		//console.log("firing", e, item)
		this.hide();
		
		// do I have to fire the handlers all the way up the tree?
		//var parent = this;
		//while (parent = parent.getFlyoutParent()) {
		
		var parent = this.getFlyoutParent();
		
		if (parent) {
			parent.onSelect().fire(item);
			parent.hide();
		}
		//}
			
	}, this);
	
	return this.onSelect();
};





// ------------------------------------------------------------
// ------------------------------------------------------------
// ------------------------------------------------------------


var FlyoutItem = function(flyout) {
	if (!flyout) {
		throw new Error("FlyoutItem Constructor: flyout is required");
	}
	this.flyout = flyout;
};

FlyoutItem.prototype.CSS_HAS_CHILD = "icon icon-arrow-right";
FlyoutItem.prototype.CSS_LAST = "last";

FlyoutItem.prototype.getHoverEvent = function() {
	var hover = this.flyout.getEventManager().add(null, "mouseover", this.hover, this);
	this.getHoverEvent = function() {
		return hover;
	};
	return this.getHoverEvent();
};

FlyoutItem.prototype.hover = function(e) {
	e.cancel();
	
	this.flyout.hover(this);
	
	if (this.child) {
		this.child.getShowEvent().fire(this.getContainer());
		//this.show()
	}
	
	WSDOM.Element.addClass(this.getContainer(), this.flyout.CSS_SELECTED);
	
	this.flyout.sizeShim(WSDOM.Element.getSize(this.flyout.getContainer()))
};

FlyoutItem.prototype.getSelectEvent = function() {
	var event = this.flyout.getEventManager().add(null, "click", this.select, this);
	
	this.getSelectEvent = function() {
		return event;
	}
	
	return this.getSelectEvent();
};

FlyoutItem.prototype.getLabel = function() {
	return null;
};

FlyoutItem.prototype.setLabel = function(label) {
	this.getLabel = function() {
		return label;
	};
	
	this.getContainer().innerHTML = label;
};

FlyoutItem.prototype.getValue = function() {
	return null;
};

FlyoutItem.prototype.setValue = function(value) {
	this.getValue = function() {
		return value;
	}
	
	if (undefined === value) {
		value = "";
	}
	
	this.getContainer().setAttribute("value", value);
};

FlyoutItem.prototype.getContainer = function(data) {
	var container = WSDOM.Element.create("li", null, null, this.flyout.getContainer());
	
	// this belongs somewhere else
	if (data.data) {
		for (var i in data.data) {
			container.setAttribute("data." + i, data.data[i]);	
		}
	}
	
	// this belongs somewhere else
	if (data.css) {
		WSDOM.Element.addClass(container, data.css);
	}
	
	
	this.getHoverEvent().addElement(container);
	this.getSelectEvent().addElement(container);
	
	this.getContainer = function() {
		return container;
	};
	
	this.setLabel(data.label);
	this.setValue(data.value);
	
	return this.getContainer();
};

FlyoutItem.prototype.setData = function(data) {
	this.data = data;
	
	var container = this.getContainer(data);
	
	if (this.child) {
		WSDOM.Element.remove(this.child.getFrame());
		WSDOM.Element.removeClass(container, this.CSS_HAS_CHILD);
		delete this.child;
	}
	
	
	if (data.children) {
		//console.log(data)
		var f = new Flyout();

		f.setFlyoutParent(this.flyout);
		f.setPositionStyle(f.POSITION_RIGHT);

		f.setData(data.children);
		
		f.addTarget(container);
		
		// this belongs somewhere else
		if (data.css) {
			WSDOM.Element.addClass(f.getFrame(), data.css);
		}
		
		WSDOM.Element.addClass(container, this.CSS_HAS_CHILD);
		
		this.child = f;
	}
};

FlyoutItem.prototype.isLast = function(isLast) {
	WSDOM.Element.switchClass(this.getContainer(), this.CSS_LAST, isLast);
};

FlyoutItem.prototype.select = function() {
	this.flyout.onSelect().fire(this);
};
	
/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/markets/InvestorRelations.js
*/

function InvestorRelations() {}

InvestorRelations.Extend(Popup);

// Constants
InvestorRelations.prototype.CSS_BOLD				= 'bold';
InvestorRelations.prototype.CSS_HIDDEN				= 'wsodHidden';
InvestorRelations.prototype.CSS_HIGHLIGHT			= 'highlight';
InvestorRelations.prototype.CSS_LOADING				= 'loading';
InvestorRelations.prototype.CSS_WIDGET				= 'investorRelationsIcons';
InvestorRelations.prototype.MAX_LENGTH_FOR_SEARCH	= 150;
InvestorRelations.prototype.DEFAULT_SORT_FIELD		= 'Name';
InvestorRelations.prototype.DEFAULT_SORT_DIR		= 'asc';
InvestorRelations.prototype.POPUP_TITLE				= 'Investor Relations Content';
InvestorRelations.prototype.LINK_URL				= '/ft/tearsheets/performance.asp';

InvestorRelations.prototype.WIDGET_TABLE_HEADERS 	= {
	"Name":{"key":"Name", "name":"Company", "css":"sortable text"},
	"Sector":{"key":"Sector", "name":"Sector", "css":"sortable text"},
	"Industry":{"key":"Industry", "name":"Industry", "css":"sortable text"},
	"Country":{"key":"Country", "name":"Country", "css":"sortable text"},
	"InvestorRelationsContent":{"key":"InvestorRelationsContent ", "name":"Investor Relations Content", "css":"text"}
};
InvestorRelations.prototype.WIDGET_TYPES 			= {
	"annualreports":{"key":"annualreports", "name":"Annual Reports", "css": "icon-widget-annualreports"},
	"events":{"key":"events", "name":"Calendar", "css": "icon-widget-events"},
	"podcast":{"key":"podcast", "name":"Podcast", "css": "icon-widget-podcast"},
	"contacts":{"key":"contacts", "name":"Contact Details", "css": "icon-widget-contacts"},
	"newsfeed":{"key":"newsfeed", "name":"News Feed", "css": "icon-widget-newsfeed"},
	"video":{"key":"video", "name":"Video", "css": "icon-widget-video"},
	"slides":{"key":"slides", "name":"Slideshow", "css": "icon-widget-slides"},
	"links":{"key":"links", "name":"Further Info", "css": "icon-widget-links"},
	"other": {"key": "other", "name": "Other", "css": "icon-widget-other" }
};
InvestorRelations.prototype.ALPHABET = [
	{"label":"Select a letter"},
	{"label":"A", "value":"a"},
	{"label":"B", "value":"b"},
	{"label":"C", "value":"c"},
	{"label":"D", "value":"d"},
	{"label":"E", "value":"e"},
	{"label":"F", "value":"f"},
	{"label":"G", "value":"g"},
	{"label":"H", "value":"h"},
	{"label":"I", "value":"i"},
	{"label":"J", "value":"j"},
	{"label":"K", "value":"k"},
	{"label":"L", "value":"l"},
	{"label":"M", "value":"m"},
	{"label":"N", "value":"n"},
	{"label":"O", "value":"o"},
	{"label":"P", "value":"p"},
	{"label":"Q", "value":"q"},
	{"label":"R", "value":"r"},
	{"label":"S", "value":"s"},
	{"label":"T", "value":"t"},
	{"label":"U", "value":"u"},
	{"label":"V", "value":"v"},
	{"label":"W", "value":"w"},
	{"label":"X", "value":"x"},
	{"label":"Y", "value":"y"},
	{"label":"Z", "value":"z"}
];

/**
 * @method init
 */
InvestorRelations.prototype.init = function() {
	this.buttonForFilter				= WSDOM.Element.get('buttonForFilter') || false;
	this.buttonForSearch				= WSDOM.Element.get('buttonForSearch') || false;
	this.currentCountry					= '';
	this.currentExchange				= '';
	this.currentLetter					= '';
	this.nameOrPartialName				= WSDOM.Element.get('nameOrPartialName') || false;
	this.matchesModule					= WSDOM.Element.get('matchesModule') || false;
	this.menuOfCountries				= WSDOM.Element.get('menuOfCountries');
	this.menuOfExchanges				= WSDOM.Element.get('menuOfExchanges');
	this.menuOfLetters					= WSDOM.Element.get('menuOfLetters');
	this.menuOfLettersDefault           = WSDOM.Element.get('menuOfLettersDefault');
	this.searchModule					= WSDOM.Element.get('searchModule') || false;
	this.wsod							= WSDOM.Element.get('wsod');
	this.irIcons						= WSDOM.Element.parseSelector("a.investorRelationsIcons", this.wsod);
	
	this.attachEvents();
	this.correctTableSort();
	
	if (this.matchesModule) {
		this.getPaging().setContentContainer(this.matchesModule);
	}
	
	if (this.searchModule) {
		this.loadingOverlay = new LoadingOverlay(this.searchModule);
	}
}


/**
 * @method attachEvents
 */
InvestorRelations.prototype.attachEvents = function() {
	if (this.buttonForSearch) {
		Events.add({element: this.buttonForSearch, context:	this, type: 'click', handler: this.searchByCompanyName});
		Events.add({element: this.nameOrPartialName, context: this, type: 'keypress', handler:	this.handleTheKeypressForSearchByCountryName});
	}

	if (this.buttonForFilter) {
		Events.add({element: this.buttonForFilter, context: this, type: 'click', handler: this.filterByCountryOrExchange});
	}
	
	if (this.irIcons && this.irIcons.length) {
		Events.add({element: this.irIcons, context: this, type: "click", handler: this.drawPopup});
	}
}

// we do this client side so that we can use TableBuilder on the server side and still have threading
InvestorRelations.prototype.correctTableSort = function() {
	for (var i = 0, parent; i < this.irIcons.length; i++) {
		parent = Element.getParent(this.irIcons[i], "td");
		
		if (parent && "TD" == parent.tagName) {
			parent.setAttribute("sortval", 1);
		}
	}
}

InvestorRelations.prototype.getPaging = function() {
	var p = new Paging();
	
	this.getPaging = function() {
		return p;
	}
	
	return this.getPaging();
}

/**
 * @method getSerializer
 */
InvestorRelations.prototype.getSerializer = function() {
	var serializer = new Serializer();
		// serializer.allowEncoding(true);

	this.getSerializer = function() {
		return serializer;
	}

	return this.getSerializer();
}

InvestorRelations.prototype.getEventManager = function() {
	var em = new EventManager();
	
	this.getEventManager = function() {
		return em;
	}
	
	return this.getEventManager();
}


/**
 * @method trim
 */
InvestorRelations.prototype.trim = function(string) {
	return string.replace(/^\s+/, '').replace(/\s+$/, '');
}


/**
 * @method handleTheKeypressForSearchByCountryName
 */
InvestorRelations.prototype.handleTheKeypressForSearchByCountryName = function(e) {
	// If the user presses the "Enter" key, run the search
	if ('13' == e.nativeEvent.keyCode) {
		var el = e.nativeEvent.target || e.nativeEvent.srcElement;
			el.blur();  // Otherwise, Firefox will put the focus back on the <INPUT>
			window.focus();  // IE 6 does not support el.blur()

		this.searchByCompanyName();
	}else{
		this.currentLetter = '';
		Element.setHTML(this.menuOfLettersDefault, "Select a letter")
		//console.log(this.nameOrPartialName.value)
	}
	
}


/**
 * @method searchByCompanyName
 */
InvestorRelations.prototype.searchByCompanyName = function() {
	if(this.currentLetter != ''){
		this.searchByLetter()
		return;
	}else{
		if (this.nameOrPartialName) {
			if (!this.nameOrPartialName.value || '' == this.trim(this.nameOrPartialName.value)) {
				alert('Please enter a company name or select a letter.');
				this.nameOrPartialName.value = '';
			}
			else if (this.nameOrPartialName.value.length > this.MAX_LENGTH_FOR_SEARCH) {
				alert('Please limit your search to ' + this.MAX_LENGTH_FOR_SEARCH + ' characters or less.');
			}
			else {
				this.setContentBufferData({
					 action:		"search"
					,companyName:	this.nameOrPartialName.value
				});

				this.loadContentBuffer();
			}
		}
	}
}

/**
 * @method searchByLetter
 */
InvestorRelations.prototype.searchByLetter = function() {
	this.setContentBufferData({
		 action:		"search"
		,searchLetter:	this.currentLetter
	});

	this.loadContentBuffer();
}


/**
 * @method filterByCountryOrExchange
 */
InvestorRelations.prototype.filterByCountryOrExchange = function() {
	var runTheFilter = false;

	var data = {
		action: "filter"
	};

	if (this.currentCountry) {
		data.country	= this.currentCountry;
		runTheFilter	= true;
	}

	if (this.currentExchange) {
		data.exchange	= this.currentExchange;
		runTheFilter	= true;
	}

	if (runTheFilter) {
		this.setContentBufferData(data);
		this.loadContentBuffer();
	}
	else {
		alert('Please select a country and/or an exchange to filter on.');
	}
}

InvestorRelations.prototype.sortResults = function(e, el) {
	var ths = Element.parseSelector("th.sortable", "tableOfMatches");
	
	var sortField = el.getAttribute("sortField");
	var sortDir = Element.hasClass(el, "asc") ? "desc" : "asc";

	Element.removeClass(ths, "sorted");
	Element.removeClass(ths, "asc");
	Element.addClass(el, "sorted "+sortDir);
	
	var data = this.getContentBufferData();
		data.sortField = sortField;
		data.sortDir = sortDir;

	this.setContentBufferData(data);
	this.loadContentBuffer(true);
}

/**
 * @method getContentBuffer
 */
InvestorRelations.prototype.getContentBuffer = function() {
	var contentBuffer = new ContentBuffer();

	this.getContentBuffer = function() {
		return contentBuffer;
	}

	return this.getContentBuffer();
}

InvestorRelations.prototype.getContentBufferData = function() { return {}; }
InvestorRelations.prototype.setContentBufferData = function(data) {
	this.getContentBufferData = function() {
		return data;
	}
}

/**
 * @method loadContentBuffer
 */
InvestorRelations.prototype.loadContentBuffer = function(disableLoading) {
	if (!disableLoading && this.loadingOverlay) {
		this.loadingOverlay.showLoading();
	}

	this.getContentBuffer().abortRequests();  // If the user happens to click several buttons, only load the most recent request
	this.getContentBuffer().load({
		contentType:	'text/javascript'
		,context:		this
		,url:			'/ft/resources/buffer/InvestorRelationsBuffer.asp'
		,method:		'post'
		,data:			this.getContentBufferData()
		,onload:		this.onBufferLoad
		,onerror:		this.onBufferError
		,preventEval:	true
		//,debug:			true
	});
}

InvestorRelations.prototype.getBufferResults = function() { return {}; }
InvestorRelations.prototype.setBufferResults = function(cb) {
	var deserializedResults = this.getSerializer().deserialize(cb.getResult());
	
	this.getBufferResults = function() {
		return deserializedResults;
	}
}

// This call does not instantiate paging.
InvestorRelations.prototype.onBufferLoadPaging = function(cb) {
	if (cb) { window.scrollTo(0, WSDOM.Element.getXY("searchModule").y); }
	
	if (cb) { this.setBufferResults(cb); }
	
	this.getEventManager().removeAll();
	
	var deserializedResults = this.getBufferResults();
	var companies = deserializedResults && deserializedResults.companies ? deserializedResults.companies : [];
	var sortField = deserializedResults && deserializedResults.sortField ? deserializedResults.sortField : this.DEFAULT_SORT_FIELD;
	var sortDir	  = deserializedResults && deserializedResults.sortDir ? deserializedResults.sortDir : this.DEFAULT_SORT_DIR;
		//console.log(deserializedResults);

	if (this.matchesModule) {
		this.matchesModule.innerHTML = '';
		WSDOM.Element.create('h2', {}, 'Company matches with Investor Relations content available', this.matchesModule);

		if (companies.length) {
			var peersAvailable = false;
			
			var tableBody = WSDOM.Element.create('tbody', {});
			
			for (var i = 0, company, companyName, currentRow, iconsToDisplay; i < companies.length; i++) {
				company = companies[i];
				companyName = !company.isPeer ? company.companyName : companyName;
				
				if (!company.isPeer) {
					currentRow	= WSDOM.Element.create('tr', {}, null, tableBody);

					WSDOM.Element.create('td', { 'class':'text' }, company.companyNameAndSymbol, currentRow);
					WSDOM.Element.create('td', { 'class':'text' }, company.sector.replace('/', ' / '), currentRow);
					WSDOM.Element.create('td', { 'class':'text' }, company.industry, currentRow);
					WSDOM.Element.create('td', { 'class':'text' }, company.country, currentRow);
					iconsToDisplay = WSDOM.Element.create('td', {}, null, currentRow);

					for (var j in this.WIDGET_TYPES) {
						if (company[j]) {
							WSDOM.Element.create('a', { 'href':this.LINK_URL+"?s="+escape(company.FTStandardSymbol)+"&widgets=1", 'class':'investorRelationsIcons '+this.WIDGET_TYPES[j].css, 'title':this.WIDGET_TYPES[j].name }, null, iconsToDisplay);
						}
					}
				} else {
					if (company.isPeer) {
						peersAvailable = true;
					}
				}
			}
			
			if (peersAvailable) {
				var peersHeader = WSDOM.Element.create('tr', {}, null, tableBody);
				WSDOM.Element.create('td', { 'class':'text', colspan:'5', 'style':'background-color:#f6e9d8; padding-left:20px' }, 'Peers of ' + companyName + ' with Investor Relations content', peersHeader);

				for (var i = 0, company; i < companies.length; i++) {
					company = companies[i];
					
					if (company.isPeer) {
						var currentRowForPeers = WSDOM.Element.create('tr', {}, null, tableBody);
	
						WSDOM.Element.create('td', { 'class':'text', 'style':'padding-left:20px' }, company.companyNameAndSymbol, currentRowForPeers);
						WSDOM.Element.create('td', { 'class':'text' }, company.sector, currentRowForPeers);
						WSDOM.Element.create('td', { 'class':'text' }, company.industry, currentRowForPeers);
						WSDOM.Element.create('td', { 'class':'text' }, company.country, currentRowForPeers);
						var iconsToDisplayForPeers = WSDOM.Element.create('td', {}, null, currentRowForPeers);
	
						for (var j in this.WIDGET_TYPES) {
							if (company[j]) {
								WSDOM.Element.create('a', { 'href':this.LINK_URL+"?s="+escape(company.FTStandardSymbol)+"&widgets=1", 'class':'investorRelationsIcons '+this.WIDGET_TYPES[j].css, 'title':this.WIDGET_TYPES[j].name }, null, iconsToDisplayForPeers);
							}
						}
					}
				}
			}
			
			var tableHead = WSDOM.Element.create('thead', {}, tableHeadRow);
			var tableHeadRow = WSDOM.Element.create('tr', {});
			
			var sorted, dir;
			for (var j in this.WIDGET_TABLE_HEADERS) {
				sorted = sortField == this.WIDGET_TABLE_HEADERS[j].key ? " sorted" : "";
				dir = sortField == this.WIDGET_TABLE_HEADERS[j].key ? " "+sortDir : "";
				
				WSDOM.Element.create('th', { 'class':this.WIDGET_TABLE_HEADERS[j].css+sorted+dir, 'sortField': this.WIDGET_TABLE_HEADERS[j].key }, [
					this.WIDGET_TABLE_HEADERS[j].name,
					Element.create('span', { 'class':'th' }, "&nbsp;")
				], tableHeadRow)
			}
			
			Element.addChild(tableHead, tableHeadRow);
		
			var currentTable	= WSDOM.Element.create('table', { id:'tableOfMatches', 'class':'sortable' }, [
				tableHead,
				tableBody
			], this.matchesModule);
		} else {
			WSDOM.Element.create('p', {}, 'Sorry, no matches were found.', this.matchesModule);
		}

		var tableOfMatches = Element.get("tableOfMatches");

		if (tableOfMatches) {
			this.getEventManager().add(Element.parseSelector("th.sortable", tableOfMatches), "click", this.sortResults, this);
		}
		
		if (this.loadingOverlay) {
			this.loadingOverlay.hideLoading();
		}
	}
}

// This call instantiates paging.
InvestorRelations.prototype.onBufferLoad = function(cb) {
	if (cb) { this.setBufferResults(cb); }

	var deserializedResults = this.getBufferResults();
	var companies = deserializedResults && deserializedResults.companies ? deserializedResults.companies : [];
	
	this.onBufferLoadPaging();
	
	if (companies && companies.length) {
 			this.getPaging().setRowsPerPage(deserializedResults.rowsPerPage);
			this.getPaging().setCurrentRow(deserializedResults.startRow);
			this.getPaging().setTotalRows(deserializedResults.totalRows);
			
			this.getPaging().setBufferLoadFunction({
				contentType:	'text/javascript'
				,context:		this
				,url:			'/ft/resources/buffer/InvestorRelationsBuffer.asp'
				,method:		'post'
				,data:			this.getContentBufferData()
				,onload:		this.onBufferLoadPaging
				,onerror:		this.onBufferError
				,preventEval:	true
				,debug:			true
			});
			
			this.getPaging().draw(); 
	}
}


/**
 * @method onBufferLoad
 */
InvestorRelations.prototype.onBufferError = function(cb) {
	var deserializedResults = this.getSerializer().deserialize(cb.getResult());
		// console.log(deserializedResults);

	if (this.matchesModule) {
		this.matchesModule.innerHTML = '';
		WSDOM.Element.create('h2', {}, 'Company matches with Investor Relations content available', this.matchesModule);
		WSDOM.Element.create('p', {}, 'Sorry, no matches were found.', this.matchesModule);
	}
}


InvestorRelations.prototype.getFlyout = function() {
	return new Flyout();
}


InvestorRelations.prototype.getCountriesAndExchanges = function() {
	return null;
}


InvestorRelations.prototype.setCountriesAndExchanges = function(countriesAndExchangesObject) {
	var countriesAndExchanges = this.getSerializer().deserialize(countriesAndExchangesObject) || {};

	this.getCountriesAndExchanges = function() {
		return countriesAndExchanges;
	}

	return this.getCountriesAndExchanges();
}


InvestorRelations.prototype.selectCountry = function(e, item) {
	this.currentCountry		= item.getValue();
	this.currentExchange	= '';

	WSDOM.Element.removeClass(this.getFlyoutControlsForCountries(), this.CSS_HIGHLIGHT);
	WSDOM.Element.addClass(item.getContainer(), this.CSS_HIGHLIGHT);

	WSDOM.Element.removeChildNodes(this.getFlyoutLabelForCountries());
	WSDOM.Element.create('span', {}, item.getLabel(), this.getFlyoutLabelForCountries());

	WSDOM.Element.removeChildNodes(this.getFlyoutLabelForExchanges());
	WSDOM.Element.create('span', {}, 'All exchanges', this.getFlyoutLabelForExchanges());

	var exchanges = this.getFlyoutControlsForExchanges();
	for (var i = 0; i < exchanges.length; i++) {
		if ('All countries' == item.getLabel()) {
			if ('All exchanges' == exchanges[i].innerHTML) {
				WSDOM.Element.addClass(exchanges[i], this.CSS_HIGHLIGHT);
			}
			else {
				WSDOM.Element.removeClass(exchanges[i], this.CSS_HIGHLIGHT);
			}

			WSDOM.Element.removeClass(exchanges[i], this.CSS_HIDDEN);
		}
		else if ('All exchanges' != exchanges[i].innerHTML && !WSDOM.Element.hasClass(exchanges[i], item.getValue())) {
			WSDOM.Element.addClass(exchanges[i], this.CSS_HIDDEN);
			WSDOM.Element.removeClass(exchanges[i], this.CSS_HIGHLIGHT);
		}
		else {
			if ('All exchanges' == exchanges[i].innerHTML) {
				WSDOM.Element.addClass(exchanges[i], this.CSS_HIGHLIGHT);
			}
			else {
				WSDOM.Element.removeClass(exchanges[i], this.CSS_HIGHLIGHT);
			}

			WSDOM.Element.removeClass(exchanges[i], this.CSS_HIDDEN);
		}
	}
}


InvestorRelations.prototype.selectExchange = function(e, item) {
	this.currentExchange	= item.getValue();

	WSDOM.Element.removeClass(this.getFlyoutControlsForExchanges(), this.CSS_HIGHLIGHT);
	WSDOM.Element.addClass(item.getContainer(), this.CSS_HIGHLIGHT);

	WSDOM.Element.removeChildNodes(this.getFlyoutLabelForExchanges());
	WSDOM.Element.create('span', {}, item.getLabel(), this.getFlyoutLabelForExchanges());

}

InvestorRelations.prototype.selectLetter = function(e, item) {
	this.currentLetter	= item.getValue();
	//console.log(this.currentLetter)
	if(this.currentLetter){
		this.nameOrPartialName.value = '';
	}

	WSDOM.Element.removeClass(this.getFlyoutControlsForLetters(), this.CSS_HIGHLIGHT);
	WSDOM.Element.addClass(item.getContainer(), this.CSS_HIGHLIGHT);

	WSDOM.Element.removeChildNodes(this.getFlyoutLabelForLetters());
	WSDOM.Element.create('span', {}, item.getLabel(), this.getFlyoutLabelForLetters());

}

InvestorRelations.prototype.drawMenuOfLetters = function() {
	var flyout = this.getFlyout();
		var shortFlyout = flyout.getFrame()
		Element.addClass(shortFlyout, "shortFlyout")
		
		flyout.setParent(this.wsod);
		flyout.addTarget(this.menuOfLetters);
		flyout.setData(this.ALPHABET);
		flyout.onSelect().addListener(
			{
				 context: this
				,handler: this.selectLetter
			}
		);

	var flyoutLabel		= this.menuOfLetters.firstChild;
	var flyoutControls	= WSDOM.Element.parseSelector('ul li', flyout.getFrame());

	this.drawMenuOfLetters = function() {
		return flyout;
	}

	this.getFlyoutLabelForLetters = function() {
		return flyoutLabel;
	}

	this.getFlyoutControlsForLetters = function() {
		return flyoutControls;
	}

	return this.drawMenuOfLetters();
}

InvestorRelations.prototype.drawMenuOfCountries = function() {
	var flyout = this.getFlyout();
		flyout.setParent(this.wsod);
		flyout.addTarget(this.menuOfCountries);
		flyout.setData(this.getCountriesAndExchanges().countries);
		flyout.onSelect().addListener(
			{
				 context: this
				,handler: this.selectCountry
			}
		);

	var flyoutLabel		= this.menuOfCountries.firstChild;
	var flyoutControls	= WSDOM.Element.parseSelector('ul li', flyout.getFrame());

	this.drawMenuOfCountries = function() {
		return flyout;
	}

	this.getFlyoutLabelForCountries = function() {
		return flyoutLabel;
	}

	this.getFlyoutControlsForCountries = function() {
		return flyoutControls;
	}

	return this.drawMenuOfCountries();
}


InvestorRelations.prototype.drawMenuOfExchanges = function() {
	var exchanges = this.getCountriesAndExchanges().exchanges;
		exchanges.sort(function(a, b) {
			return (a.label < b.label) ? -1 : 1;
		});

	var flyout = this.getFlyout();
		flyout.setParent(this.wsod);
		flyout.addTarget(this.menuOfExchanges);
		flyout.setData(exchanges);
		flyout.onSelect().addListener(
			{
				 context: this
				,handler: this.selectExchange
			}
		);

	var flyoutLabel		= this.menuOfExchanges.firstChild;
	var flyoutControls	= WSDOM.Element.parseSelector('ul li', flyout.getFrame());

	this.drawMenuOfExchanges = function() {
		return flyout;
	}

	this.getFlyoutLabelForExchanges = function() {
		return flyoutLabel;
	}

	this.getFlyoutControlsForExchanges = function() {
		return flyoutControls;
	}

	return this.drawMenuOfExchanges();
}

InvestorRelations.prototype.getActiveSymbol = function() { return ""; }
InvestorRelations.prototype.setActiveSymbol = function(s) {
	this.getActiveSymbol = function() {
		return s;
	}
}

InvestorRelations.prototype.position = function (e, el) {
    var contentWell = WSDOM.Element.getXY(this.getFrame().offsetParent);
	var iconSize = WSDOM.Element.getSize(el);
	
    var view = WSDOM.Element.getViewport();
	var frame = this.getFrame();

    var frameSize = WSDOM.Element.getSize(frame);

    var frameTop = view.top + e.nativeEvent.clientY;//Math.max(view.top + ( view.height / 2 ) - ( frameSize.height / 2 ), 0) - contentWell.y;
    var frameLeft = e.nativeEvent.clientX + iconSize.width;//Math.max(( view.width / 2 ) - ( frameSize.width / 2 ), 0) - contentWell.x;

    WSDOM.Element.setXY(frame, frameLeft, frameTop);
	this.sizeShim();
	// add a class instead
    frame.style.visibility = 'visible';
};

InvestorRelations.prototype.close = function (e, el) {
	this.setActiveSymbol("");

	if(e){ e.cancel() };

	var frame = this.getFrame();
	
	if (!frame) {
		return;
	}
	
    WSDOM.Element.addClass(frame, this.CSS_HIDDEN);
	frame.style.visibility = "hidden";
};

// this is a non-dragable module
InvestorRelations.prototype.dragStart = function() {
	return false;
}

InvestorRelations.prototype.getCloseLink = function () {
	var closeEvent = this.getCloseEvent();
	var closeLink = Element.create('a', { href: '#', className: 'closeLink' }, '&nbsp;'); 
	
	closeEvent.addElement(closeLink);
	this.getCloseLink = function() {
		
		return closeLink;
	
	};

	return this.getCloseLink();
};

InvestorRelations.prototype.drawPopup = function(e, el) {
	var symbol = el.getAttribute("ftStandard");
	var widgets = [];
	//for (var i = 0, isActive; i < this.WIDGET_TYPES.length; i++) {
	var isActive;
	for (var i in this.WIDGET_TYPES) {
		isActive = Number(el.getAttribute(this.WIDGET_TYPES[i].key));
		
		if (isActive) {
			widgets.push(
				WSDOM.Element.create("li", {"class":"contain"}, [
					WSDOM.Element.create("a", {"href":"/ft/tearsheets/performance.asp?s="+symbol+"&widgets=1", "class":this.CSS_WIDGET + " " + this.WIDGET_TYPES[i].css}, this.WIDGET_TYPES[i].name)
				])
			);
		}
	}
	
	this.setTitleText(this.POPUP_TITLE);
	
	if (symbol == this.getActiveSymbol()) {
		this.getCloseEvent().fire();
		
		this.setActiveSymbol("");
	} else {
		this.clearContent();
		this.getCloseEvent().fire();
		
		this.setActiveSymbol(symbol);
		
		WSDOM.Element.create("div", null, [
			WSDOM.Element.create("ul", {}, widgets)
		], this.getContent());
		
		WSDOM.Element.addClass(this.getFrame(), "investorRelationsPopup");
		
		if (this.isVisible()) {
			return;
		}

		if (!this.allowOthers() && Popup.instances) {
			for( var i = 0; i < Popup.instances.length; i++ )
			{
				Popup.instances[i].close();
			}
		}

		var frame = this.getFrame();
		WSDOM.Element.removeClass(frame, this.CSS_HIDDEN);

		this.position(e, el);
		
		this.sizeShim();
	}
}

InvestorRelations.prototype.draw = function() {
	this.drawMenuOfCountries();
	this.drawMenuOfExchanges();
	this.drawMenuOfLetters();
}

/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/SortableTable.js
*/
function SortableTable(table,rowOfHead)
{
	var rThead = Element.parseSelector("thead", table);
	this.list = [];

	for (var h=0; h<rThead.length; h++)
	{
		var tbody = Element.parseSelector("+tbody",rThead[h],"first");
		if (tbody) {
			this.list.push(new _SortableTable(rThead[h],tbody,rowOfHead));
		}
	}
}
SortableTable.prototype.init = function(table,rowOfHead)
{
	return new SortableTable(table, rowOfHead);
}

function _SortableTable(tHead, tBody, rowOfHead) {
	if (tHead && tBody) {
		this.eventManager = new EventManager();
		this.init(tHead, tBody, rowOfHead);
	}
};

_SortableTable.prototype.init = function(tHead, tBody, rowOfHead) {

	this.table = tHead && tHead.parentNode
		|| this.tHead && this.tHead.parentNode
		|| this.table;

	this.thead = tHead; //Element.parseSelector("thead", this.table, "first");
	this.tbody = tBody; //Element.parseSelector("tbody", this.table, "first");

	this.colHeads = Element.parseSelector("th", this.thead);
	this.sortableColHeads = Element.parseSelector("th[" + this.ATTR_DEFAULT_SORT_DIR + "]", this.thead);
	if(this.thead.WSOD_clones) {
		for(var i = 0; i < this.thead.WSOD_clones.length; i++) {
			this.sortableColHeads = this.sortableColHeads.concat(Element.parseSelector("th[" + this.ATTR_DEFAULT_SORT_DIR + "]", this.thead.WSOD_clones[i]));
		}
	}
	this.rows = Element.parseSelector("tr", this.tbody);

	this.lastSort = -1; // necessary?

	this.rowOfHead = rowOfHead || this.rowOfHead || 0;
	this.addEventHandlers();

};

_SortableTable.prototype.ATTR_DEFAULT_SORT_DIR = "sortdir";
_SortableTable.prototype.ATTR_USE_SORT_FIELD = "usesortfield";
_SortableTable.prototype.ATTR_SORT_VALUE = "sortval";
_SortableTable.prototype.CSS_SORTED = "sorted";
_SortableTable.prototype.CSS_LEFT_ALIGN = "left";
_SortableTable.prototype.CSS_SORTED_ASC = "asc";
_SortableTable.prototype.CSS_SORTED_DESC = "desc";

_SortableTable.prototype.addEventHandlers = function() {
	if (this.sortEvents) {
		this.sortEvents.removeAllElements();
	}

	this.sortEvents = this.eventManager.add(this.sortableColHeads, "click", this._sort, this);
};

_SortableTable.prototype._sort = function(e, el) {
	var addClass, replaceClass;
	
	var getCellIndex = function(el) {
		var row = el.parentNode;
		for(var i = 0; i < row.cells.length; i++) 
			if(row.cells[i] == el) return i;
		return -1;
	};
	var getOriginalCell = function(el) {
		return getRowGroup(el).WSOD_original.rows[0].cells[getCellIndex(el)];
	};
	var getRowGroup = function(el){
		return el.parentNode.parentNode;
	};
	var getCellClones = function(el) {
		var cellIndex = getCellIndex(el);
		var clones = [], row;
		for(var i = 0; i < getRowGroup(el).WSOD_clones.length; i++) {
			row = getRowGroup(el).WSOD_clones[i].rows[0];
			if (row.cells.length > cellIndex) {
				clones.push(row.cells[cellIndex]);
			}
		}
		return clones;
	};
	
	if (getRowGroup(el).WSOD_original) {
		el = getOriginalCell(el);
	}
	
	if (el.parentNode.parentNode.WSOD_original || el.parentNode.parentNode.WSOD_clones) {
		addClass = function(el, className) {
			Element.addClass(el, className);
			Element.addClass(getCellClones(el), className);
		};
		removeClass = function(el, className) {
			Element.removeClass(el, className);
			Element.removeClass(getCellClones(el), className);
		};
		replaceClass = function(el, oldClass, newClass) {
			Element.replaceClass(el, oldClass, newClass);
			Element.replaceClass(getCellClones(el), oldClass, newClass);
		};
	} else {
		addClass = function(el, className) {
			Element.addClass(el, className);
		};
		removeClass = function(el, className) {
			Element.removeClass(el, className);
		};
		replaceClass = function(el, oldClass, newClass) {
			Element.replaceClass(el, oldClass, newClass);
		};
	}

	if(el.cellIndex == this.lastSort || Element.hasClass(el, this.CSS_SORTED)) {

		if (Element.hasClass(el, this.CSS_SORTED_ASC)) {
			replaceClass(el, this.CSS_SORTED_ASC, this.CSS_SORTED_DESC);
		} else {
			replaceClass(el, this.CSS_SORTED_DESC, this.CSS_SORTED_ASC);
		}

		this.rows.reverse();

	} else {

		for (var i=0; i<this.colHeads.length; i++) {
			removeClass(this.colHeads[i], this.CSS_SORTED);
		}

		this._sortRows(el);

		addClass(el, this.CSS_SORTED);
		// not sorted, so use default
		if (this.CSS_SORTED_ASC == el.getAttribute(this.ATTR_DEFAULT_SORT_DIR)) {
			addClass(el, this.CSS_SORTED_ASC);
		}
		else {
			addClass(el, this.CSS_SORTED_DESC);
			this.rows.reverse();
		}

	}

	this.lastSort = el.cellIndex;
	this._redrawTable();
};

_SortableTable.prototype._sortRows = function(el) {
	var sortValue;
	if ("true" == el.getAttribute(this.ATTR_USE_SORT_FIELD)) {
		sortValue = this.ATTR_SORT_VALUE;
	}

	var cellIndex = el.cellIndex;
	if (el.cellIndex > 0 && this.sortableColHeads[el.cellIndex-1] && this.sortableColHeads[el.cellIndex-1].getAttribute("colSpan")) {
		 cellIndex = el.cellIndex + (this.sortableColHeads[el.cellIndex-1].getAttribute("colSpan") - 1);
	}
	

	function sorter(a,b) {
		if (sortValue) {
			var aa = String(a.cells[cellIndex].getAttribute(sortValue)).toLowerCase();
			var bb = String(b.cells[cellIndex].getAttribute(sortValue)).toLowerCase();
		}
		else {
			var aa = String(a.cells[cellIndex].innerHTML).toLowerCase();
			var bb = String(b.cells[cellIndex].innerHTML).toLowerCase();
		}

		var numAA = parseFloat(aa);
		var numBB = parseFloat(bb);

		if (!isNaN(numAA) && !isNaN(numBB)) {
			return numAA - numBB;
		}
		else if (aa>bb) {
			return 1;
		}
		else if (aa<bb) {
			return -1;
		}

		return 0;
	}

	this.rows.sort(sorter);
};

_SortableTable.prototype._redrawTable = function() {
	Element.removeChildNodes(this.tbody);
	for (var i=0; i<this.rows.length; i++) {
		Element.addChild(this.tbody, this.rows[i]);
	}
	if(this.tbody.WSOD_clone) {
		Element.removeChildNodes(this.tbody.WSOD_clone);
		for (var i=0; i<this.rows.length; i++) {
			Element.addChild(this.tbody.WSOD_clone, this.rows[i].WSOD_clone);
		}
	}
};

SortableTable.sortables = [];
SortableTable.initAllSortableTables = function(TableConstructor, tables) {
	TableConstructor = TableConstructor || SortableTable;
	tables = tables || Element.parseSelector("table.sortable");

	for (var i=0; i<tables.length; i++) {
		
		if (this.sortables[i]) {
			//already exists, reuse it
			var thead = Element.parseSelector("thead", tables[i], "first");
			var tbody = Element.parseSelector("tbody", tables[i], "first");
			this.sortables[i].init(thead, tbody);
		}
		else {
			this.sortables.push(new TableConstructor(tables[i]));
		}
	}
	return this.sortables;

};

GroupedSortableTable = function(table, rowOfHead) {
	rowOfHead = rowOfHead || 0;
	GroupedSortableTable.Super(this, null, [
		Element.parseSelector("thead", table)[rowOfHead], 
		Element.parseSelector("tbody", table, "first")
	]);
};
GroupedSortableTable.Extend(_SortableTable);

GroupedSortableTable.prototype.init = function(table, rowOfHead) {
	GroupedSortableTable.Super(this, "init", arguments);

	this.tbodys = Element.parseSelector("tbody", this.table);
};

GroupedSortableTable.prototype._sort = function(e, el) {
	var dir = -1;
	if (Element.hasClass(el, this.CSS_SORTED)) {
		if (Element.hasClass(el, this.CSS_SORTED_ASC)) {
			Element.replaceClass(el, this.CSS_SORTED_ASC, this.CSS_SORTED_DESC);
		} else {
			Element.replaceClass(el, this.CSS_SORTED_DESC, this.CSS_SORTED_ASC);
			dir = 1;
		}
	}
	else {
		if (this.colHeads[this.lastSort]) {
			Element.removeClass(this.colHeads[this.lastSort], this.CSS_SORTED);
		}

		Element.addClass(el, this.CSS_SORTED);

		if (this.CSS_SORTED_ASC == el.getAttribute(this.ATTR_DEFAULT_SORT_DIR)) {
			Element.addClass(el, this.CSS_SORTED_ASC);
			dir = 1;
		}
		else {
			Element.addClass(el, this.CSS_SORTED_DESC);
		}
	}

	this.lastSort = el.cellIndex;
	this._sortRows(el, dir);
};

GroupedSortableTable.prototype._sortRows = function(el, dir) {
	var sortValue;
	var sortFunction = this._sorter;

	if ("true" == el.getAttribute(this.ATTR_USE_SORT_FIELD)) {
		sortValue = this.ATTR_SORT_VALUE;
	}

	var groupSortRows = [];
	for (var i=0; i<this.tbodys.length; i++) {
		var rows = Element.parseSelector("tr", this.tbodys[i]);
		if (rows.length) {

			function sorter(a, b) {
				return sortFunction(rows, el, sortValue, dir, true, a, b);
			};
			rows = rows.sort(sorter);


			Element.removeChildNodes(this.tbodys[i]);
			for (var j=0; j<rows.length; j++) {
				Element.addChild(this.tbodys[i], rows[j]);
			}
			groupSortRows.push(rows[0]);
		}
	}

	function sortGroups(a, b) {
		return sortFunction(null, el, sortValue, dir, false, a, b);
	};
	groupSortRows = groupSortRows.sort(sortGroups);

	for (var i=0; i<groupSortRows.length; i++) {
		var tbody = groupSortRows[i].parentNode;
		Element.remove(tbody);
		Element.addChild(this.table, tbody);
	}

};

GroupedSortableTable.prototype._sorter = function(rows, el, sortValue, dir, preserveFirst, a, b) {
	if (preserveFirst) {
		if (rows[0] == a) {
			return -1;
		}
		else if (rows[0] == b) {
			return 1;
		}
	}

	if (sortValue) {
		var aa = String(a.cells[el.cellIndex].getAttribute(sortValue)).toLowerCase();
		var bb = String(b.cells[el.cellIndex].getAttribute(sortValue)).toLowerCase();
	}
	else {
		var aa = String(a.cells[el.cellIndex].innerHTML).toLowerCase();
		var bb = String(b.cells[el.cellIndex].innerHTML).toLowerCase();
	}

	var numAA = parseFloat(aa);
	var numBB = parseFloat(bb);

	if (!isNaN(numAA) && !isNaN(numBB)) {
		return (numAA - numBB) * dir;
	}
	else if (aa>bb) {
		return 1 * dir;
	}
	else if (aa<bb) {
		return -1 * dir;
	}

	return 0;
};

GroupedSortableTable.prototype._redrawTable = function() {
};

/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/Paging.js
*/
/*
	Usage:
		After instantiating this object, you must call three functions before it will function correctly, they are:
		
			.setContentContainer()
			.setBufferLoadFunction()
			.init() or .draw()
		
		.setContentContainer() must be passed the parent element that contains the paging and the content that is being rendered per page.
		
		.setBufferLoadFunction() must be passed your content buffer call exactly as you would pass it to a .load call on ContentBuffer.
		
		.init() or .draw() must be called to attach events to the rendered paging.
		
		If you are using this in conjunction with the server side version, pass in zero parameters to the constructor.  This library will grab all the key values from the previously rendered paging.
  		
		Here's an example usage:
		
			if NOT using server side paging:
				var p = new Paging({r: currentRow, t: totalRows, n: numRowsPerPage});
				- or -
				var p = new Paging(currentRow, totalRows, numRowsPerPage);
			
			if using server side paging:
				var p = new Paging();
				
				p.setContentContainer(Element.get("wsod"));
				p.setBufferLoadFunction({
					url: "PagingTest_buffer.asp",
					method: "post",
					contentType: "text/html",
					data: {
						wsodIssue: 572009
					},
					debug: true,
					onload: this.outputResults,
					context: this
				});
				p.init();
				
		If you want to disable the loading graphic, call the following:
		
			p.showLoading(false);
*/
var Paging = function() {
	var currentRow, totalRows, numRowsPerPage;
	
	if (arguments.length > 1) {
		currentRow = arguments[0];
		totalRows = arguments[1];
		numRowsPerPage = arguments[2] || this.DEFAULT_ROWS_PER_PAGE;
	} else if (1 == arguments.length && "object" == typeof arguments[0]) {
		currentRow = arguments[0].r;
		totalRows = arguments[0].t;
		numRowsPerPage = arguments[0].n || this.DEFAULT_ROWS_PER_PAGE;
	}

	this.setRowsPerPage(numRowsPerPage);
	this.setCurrentRow(currentRow);
	this.setTotalRows(totalRows);
	this.showLoading(true);
}

Paging.prototype.DEFAULT_ROWS_PER_PAGE = 25;
Paging.prototype.DEFAULT_URL = "javascript:void(0)";
Paging.prototype.VISIBLE_PAGES = 5;
Paging.prototype.CSS_PAGING_LEFT_ACTIVE = "nav-icon nav-left-active";
Paging.prototype.CSS_PAGING_RIGHT_ACTIVE = "nav-icon nav-right-active";
Paging.prototype.CSS_PAGING_LEFT_INACTIVE = "nav-icon nav-left-inactive";
Paging.prototype.CSS_PAGING_RIGHT_INACTIVE = "nav-icon nav-right-inactive";

Paging.prototype.showLoading = function(show) {
	if (undefined != show) {
		this._showLoading = show;
	}
	
	return this._showLoading || false;
}

Paging.prototype.getContentContainer = function() { return document.body; }
Paging.prototype.setContentContainer = function(elContainer) {
	this.getContentContainer = function() {
		return "string" == typeof elContainer ? Element.get(elContainer) : elContainer || document.body;
	}
	
	this.setPagingContainer();
}

Paging.prototype.getPagingContainer = function() { return null; }
Paging.prototype.setPagingContainer = function(container) {
	var container = container || Element.parseSelector("div.pagingNavigation", this.getContentContainer(), "first");
	
	if (container) {
		this.getPagingContainer = function() {
			return container;
		}
		
		if (!this.getCurrentRow() && !this.getTotalRows()) {
			this.setRowsPerPage(this.getPagingContainer().getAttribute("rowsPerPage"));
			this.setCurrentRow(this.getPagingContainer().getAttribute("currentRow"));
			this.setTotalRows(this.getPagingContainer().getAttribute("totalRows"));	
		}
	}
}

Paging.prototype.getBufferLoadFunction = function() { alert('setBufferLoadFunction must be called.'); return null; }
Paging.prototype.setBufferLoadFunction = function(loadParams) {
	var onloadFunction = loadParams.onload || function() {};
	var onloadContext = loadParams.context || window;
	
	loadParams = loadParams || {};
	loadParams.data = loadParams.data || {};
	loadParams.onload = function(cb) {
		onloadFunction.Context(onloadContext)(cb);
		
		if (this.showLoading()) { this.removeLoading(); }
		this.remove();

		Element.addChild("wsod", this.draw());
		
		MouseHover.Symbol.init();
	}.Context(this);

	this.getBufferLoadFunction = function() {
		return loadParams;
	}
}

Paging.prototype.getPageContent = function(e, el) {
	var rowsPerPage = this.getRowsPerPage();
	var page = Number(el.getAttribute("page"));
	
	this.setCurrentRow(rowsPerPage * (page - 1) + this.getBaseRow());

	var loadParams = this.getBufferLoadFunction();
		loadParams.data.startRow = this.getCurrentRow();

	if (this.showLoading()) { this.drawLoading(); }
		
 	this.getContentBuffer().abortRequests();
 	this.getContentBuffer().load(loadParams);
}

Paging.prototype.drawLoading = function() {
	var contentContainer = this.getContentContainer();
	var contentContainerSize = Element.getSize(contentContainer);
	var pagingContainer = this.getPagingContainer();
	var pagingContainerSize = Element.getSize(pagingContainer);
	
	// this feels risky, but for the loading functionality to work correctly, the container needs to be position: relative
	if (!contentContainer.style.position) {
		contentContainer.style.position = "relative";
	}
	
	var loadingImageContainerEl;
	var loadingEl = Element.create("div", {"class":"loadingContainer wsodHidden"}, [
		loadingImageContainerEl = Element.create("div", {"class":"loadingImageContainer"}, [
			Element.create("div", {"class":"loadingImage"}, "&nbsp;")
			//,Element.create("div", {"class":"loadingText"}, "Loading...")
		])
	], contentContainer);
	
	var loadingImageContainerElSize = Element.getSize(loadingImageContainerEl);
	
	Element.setSize(loadingEl, contentContainerSize.width, contentContainerSize.height);
	Element.setXY(loadingImageContainerEl, null, (contentContainerSize.height / 2) - (loadingImageContainerElSize.height / 2));
	Element.removeClass(loadingEl, "wsodHidden");
}

Paging.prototype.removeLoading = function() {
	var loadingEl = Element.parseSelector("div.loadingContainer", this.getContentContainer(), "first");
	
	if (loadingEl) {
		Element.remove(loadingEl);
	}
}

Paging.prototype.getCurrentRow = function() { return null; }
Paging.prototype.setCurrentRow = function(row) {
	this.setBaseRow(row);

	this.getCurrentRow = function() {
		return row;
	}
}

Paging.prototype.getBaseRow = function() { return null; }
Paging.prototype.setBaseRow = function(row) {
	var baseRow = row ? row % this.getRowsPerPage() : null;

	this.getBaseRow = function() {
		return baseRow;
	}
}

Paging.prototype.getRowsPerPage = function() { return null; }
Paging.prototype.setRowsPerPage = function(rows) {
	this.getRowsPerPage = function() {
		return rows;
	}
}

Paging.prototype.getTotalRows = function() { return null; }
Paging.prototype.setTotalRows = function(rows) {
	this.getTotalRows = function() {
		return rows;
	}
}

Paging.prototype.getCurrentPage = function() {
	var currentRow = this.getCurrentRow();
	var rowsPerPage = this.getRowsPerPage();
	
	var currentPage = Math.floor(currentRow / rowsPerPage) + 1;

	return currentPage;
}

Paging.prototype.getStartPage = function() {
	var numPages = this.getNumPages();
	var currentPage = this.getCurrentPage();
	var midPoint = Math.round(this.VISIBLE_PAGES / 2);
	
	var startPage;
	
	if (currentPage <= midPoint) {
		startPage = 1;
	} else if (currentPage >= numPages - midPoint + 1) {
		startPage = numPages - this.VISIBLE_PAGES + 1;
	} else {
		startPage = currentPage - midPoint + 1;
	}
	
	return startPage;
}

Paging.prototype.getEndPage = function() {
	var numPages = this.getNumPages();
	var currentPage = this.getCurrentPage();
	var midPoint = Math.round(this.VISIBLE_PAGES / 2);
	
	var endPage;
	
	if (currentPage <= midPoint) {
		endPage = numPages < this.VISIBLE_PAGES ? numPages : this.VISIBLE_PAGES;
	} else if (currentPage >= numPages - midPoint + 1) {
		endPage = numPages;
	} else {
		endPage = currentPage + midPoint - 1;
	}
	
	return endPage;
}

Paging.prototype.getNumPages = function() {
	var perPage = this.getRowsPerPage();
	var totalRows = this.getTotalRows();
	
	var numPages = Math.ceil(totalRows / perPage);
	
	return numPages;
}

Paging.prototype.getPreviousLink = function() {
	var startPage = this.getStartPage();
	var currentPage = this.getCurrentPage();
	
	var targetPage = Math.max(currentPage - 1, startPage);
	
	var previousLink;
	if (startPage == currentPage) {
		previousLink = Element.create("div", {"class":this.CSS_PAGING_LEFT_INACTIVE});
	} else {
		previousLink = Element.create("a", {"class":this.CSS_PAGING_LEFT_ACTIVE, "href":"javascript:void(0)", "page": targetPage});
	}
	
	return previousLink;
}

Paging.prototype.getNextLink = function() {
	var endPage = this.getEndPage();
	var currentPage = this.getCurrentPage();
	
	var targetPage = Math.min(currentPage + 1, endPage);
	
	var nextLink;
	if (endPage == currentPage) {
		nextLink = Element.create("div", {"class":this.CSS_PAGING_RIGHT_INACTIVE});
	} else {
		nextLink = Element.create("a", {"class":this.CSS_PAGING_RIGHT_ACTIVE, "href":"javascript:void(0)", "page": targetPage});
	}
	
	return nextLink;
}

Paging.prototype.getContent = function() {
	var content = Element.parseSelector("div.pagingNavigation", this.getContentContainer(), "first");
	
	if (!content) {
		var currentRow = this.getCurrentRow();
		var totalRows = this.getTotalRows();
		var rowsPerPage = this.getRowsPerPage();
		var currentPage = this.getCurrentPage();
		
		var nextLink, previousLink;
		var content = Element.create("div", {"class":"pagingNavigation"}, [
			nextLink = this.getNextLink(),
			previousLink = this.getPreviousLink(),
			Element.create("div", {"class":"pagingKey"}, [
				Element.create("span", {}, (currentRow + (this.getBaseRow() ? 0 : 1)) + "-" + Math.min(rowsPerPage * currentPage, totalRows)),
				" of " + totalRows + " results"
			])
		], this.getContentContainer());
		
		var pagingLinks = [];
		if ("A" == nextLink.tagName) { pagingLinks.push(nextLink); }
		if ("A" == previousLink.tagName) { pagingLinks.push(previousLink); }
		
		this.getPagingLinks = function() {
			return pagingLinks;
		}
	}
	
	this.setPagingContainer(content);
	
	return content;
}

Paging.prototype.getPagingLinks = function() {
	return Element.parseSelector("a", this.getPagingContainer());
}

Paging.prototype.draw = function() {
	var content = this.getContent();
	
	this.getEventManager().add(this.getPagingLinks(), "click", this.getPageContent, this);	
}

Paging.prototype.init = Paging.prototype.draw;

Paging.prototype.remove = function() {
	this.getEventManager().removeAll();

	Element.removeChildNodes(this.getPagingContainer());
}

Paging.prototype.getContentBuffer = function() {
	var cb = new ContentBuffer();
	
	this.getContentBuffer = function() {
		return cb;
	}
	
	return this.getContentBuffer();
}

Paging.prototype.getEventManager = function() {
	var em = new EventManager();
	
	this.getEventManager = function() {
		return em;
	}
	
	return this.getEventManager();
}